/*
 *  Copyright 2011 yura.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package org.opu.db_vdumper.ui.component.tabel.views;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import org.opu.db_vdumper.ui.component.AbsoluteLayout;
import org.opu.db_vdumper.ui.component.PDimension;
import org.opu.db_vdumper.ui.component.PreferredLocation;
import org.opu.db_vdumper.ui.component.tabel.ArrowMethod;
import org.opu.db_vdumper.ui.component.tabel.models.ArrowCalculator;
import org.opu.db_vdumper.ui.component.tabel.models.ArrowModel;
import org.opu.db_vdumper.ui.component.tabel.models.SelectedComponent;
import org.opu.db_vdumper.ui.component.tabel.models.SelectedComponentImpl;

/**
 *  Create instance of class using {@link ArrowMethod}.<br/>
 *  The contains a 2 node for for flexible connection of components:
 * <pre>
 *      ---------
 *      |       |&lt;-------------#
 *      ---------              |
 *                             |
 *                  ---------  |
 *                  |       |--#
 *                  ---------
 * </pre>
 *  Each of them can move, and arrow have changed
 * @author Potochnak Yana
 */
public class ArrowView extends SelectedComponentImpl<ArrowModel> implements PreferredLocation{
    private ArrowModel model;

    /** Where to view */
    private TableView from;

    /** Where from view */
    private TableView to;

    /** View maker component: break point line */
    private Point fromConnect, firstNode, secondNode, toConnect;

    /** Picks view at the start and end */
    private PikeView pickFrom, pickTo;

    /** where must be locate curent arrow-view */
    private Point location;

    /** preferded size of curent view */
    private Dimension size;
    
    /**
     * 
     * @param model
     * @param from
     * @param to
     */
    public ArrowView(ArrowModel model, TableView from, TableView to) {
        setLayout(new AbsoluteLayout());
        this.model = model;
        this.from = from;
        this.to = to;
        this.model.addViews(ArrowView.this);
        this.init();
    }

    @Override
    public ArrowModel getViewModel() {
        return model;
    }

    public void setModel(ArrowModel model) {
        this.model = model;
    }


    public TableView getFrom() {
        return from;
    }

    public TableView getTo() {
        return to;
    }

    public void setFrom(TableView from) {
        this.from = from;
        init();
    }

    public void setTo(TableView to) {
        this.to = to;
        init();
    }


    public Point getFirstNode() {
        return firstNode;
    }

    public Point getFromConnect() {
        return fromConnect;
    }

    public Point getSecondNode() {
        return secondNode;
    }

    public Point getToConnect() {
        return toConnect;
    }

    public void setFirstNode(Point firstNode) {
        this.firstNode = firstNode;
        repaint();
    }

    public void setFromConnect(Point fromConnect) {
        this.fromConnect = fromConnect;
        repaint();
    }

    public void setSecondNode(Point secondNode) {
        this.secondNode = secondNode;
        repaint();
    }

    public void setToConnect(Point toConnect) {
        this.toConnect = toConnect;
        repaint();
    }


    public PikeView getPickFrom() {
        return pickFrom;
    }

    public PikeView getPickTo() {
        return pickTo;
    }

    public void setPickFrom(PikeView pickFrom) {
        if (pickFrom != null && pickFrom != this.pickFrom && pickFrom != this.pickTo) {
            remove(this.pickFrom);
            this.pickFrom = pickFrom;

            int lx = location.x, ly = location.y;
            add(pickFrom, new PDimension(fromConnect.x - lx, fromConnect.y - ly));

            repaint();
        }

    }

    public void setPickTo(PikeView pickTo) {
        if (pickFrom != null && pickFrom != this.pickFrom && pickFrom != this.pickTo) {
            remove(this.pickTo);
            this.pickTo = pickTo;

            int lx = location.x, ly = location.y;
            Point l = pickTo.locate();
            add(pickTo, new PDimension(toConnect.x - lx - l.x, toConnect.y - ly - l.y));

            repaint();
        }
    }

    @Override
    public void revalidate() {
        throw new IllegalArgumentException();
        
    }

    public void reinit(){
        this.init();
    }

    /**
     *  Initialize all properties
     */
    private void init(){
        ArrowCalculator calc = new ArrowCalculator();
        calc.connectionPoint(model, from, to);

        fromConnect = calc.getFromConnect();
        firstNode = calc.getFirstNode();
        secondNode = calc.getSecondNode();
        toConnect = calc.getToConnect();
        pickFrom = calc.getPickFrom();
        pickTo = calc.getPickTo();

        int[] arr = calc.borderPoints();
        location = new Point(arr[0] - 2, arr[1] - 2);
        size = new Dimension(arr[2] - arr[0] + 4, arr[3] - arr[1] + 4);
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;

        int lx = location.x, ly = location.y;
        Point l = pickTo.locate();
        pickFrom.draw(g2, new Point(fromConnect.x - lx, fromConnect.y - ly));
        pickTo.draw(g2, new Point(toConnect.x - lx, toConnect.y - ly));

        Point f = fromConnect;
        Point n1 = firstNode;
        Point n2 = secondNode;
        Point t = toConnect;

        int fx = f.x - lx;
        int fy = f.y - ly;
        int n1x = n1.x - lx;
        int n1y = n1.y - ly;
        int n2x = n2.x - lx;
        int n2y = n2.y - ly;
        int tx = t.x - lx;
        int ty = t.y - ly;

        Image toDraw = getImageBorder();
        if (toDraw != null) {
            int st = toDraw.getWidth(null);
            g2.drawImage(toDraw, n1x - 2, n1y - 2, st, st, null);
            g2.drawImage(toDraw, n2x - 2, n2y - 2, st, st, null);

            int y0 = -1;
            y0 = drawBorder(fx, n1x, st, fy, n1y, g2, toDraw, y0);
            y0 = drawBorder(n1x, n2x, st, n1y, n2y, g2, toDraw, y0);
            y0 = drawBorder(n2x, tx, st, n2y, ty, g2, toDraw, y0);
        }

        g2.drawLine(fx, fy, n1x, n1y);
        g2.drawLine(n1x, n1y, n2x, n2y);
        g2.drawLine(n2x, n2y, tx, ty);

        g2.fillRect(n1x - 1, n1y - 1, 4, 4);
        g2.fillRect(n2x - 1, n2y - 1, 4, 4);
    }

    /**
     *    Draw line from images
     * @param x1 the x coordinate of line beginning
     * @param x2 the x coordinate of line end
     * @param st drawing width and height
     * @param y1 the y coordinate of line beginning
     * @param y2 the y coordinate of line end
     * @param g2 graphic environment where it will be a drawing
     * @param toDraw the specified image to be drawn
     * @param y0 priviosly y coordinate of drawing (to draw horisontal
     *      line, becouse drawing is done on the X coordinate)
     * @return y0 the last y coordinate of drawing
     */
    protected int drawBorder(int x1, int x2, int st, int y1, int y2,
            Graphics2D g2, Image toDraw, int y0) {
        int y = -1;
        boolean b = true;

        for (int x = Math.min(x1, x2); x < Math.max(x1, x2); x += st) {
            b = false;
            try {
                y = Geometry.linePassingTwoPoints(new Point(x1, y1), new Point(x2, y2), x);
            } catch (ArithmeticException e) {
            }
            int h = st;
            if (y0 >= 0 && y != y0) {
                g2.drawImage(toDraw, x - st / 2, Math.min(y, y0), st, Math.abs(y - y0), null);
            } else {
                g2.drawImage(toDraw, x, y, st, st, null);
            }
            y0 = y;
        }
        if (b){
            int h = Math.abs(y2 - y1);
            y1 = Math.min(y1, y2);
            g2.drawImage(toDraw, x1, y1, st, h, null);
            y0 = y2;
        }
        return y0;
    }

    @Override
    public Dimension getPreferredSize() {
        return //new Dimension(500, 500);
        size;
    }

    @Override
    public Point getPreferredLocation() {
        return location;
    }

    @Override
    public void reset() {
        repaint();
    }
}
